package socket;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 聊天室服务端
 */
public class Server {
    /*
    java.ner.ServerSocket
    运行在服务端，主要作用有两个：
    1.向系统申请服务端口，客户端可以通过该端口与服务端建立连接
    2.监听服务端口，一旦一个客户端连接了，就会立即建立一个Socket实例与该客户端交互
     */
    private ServerSocket serverSocket;//总机
    public Server(){//构造器
        try {
            System.out.println("正在启动服务器");
            /*
            创建ServerSocket对象时，会自动向系统申请一个端口，并监听该端口
            如果该端口被其他程序占用，则会抛出异常
            java.net.BindException: Address already in use
            解决办法：
            1.杀死占用该端口的进程(通常发生在服务端启动两次导致)
            2.更换端口号
             */
            serverSocket =new ServerSocket(9089);//0-1024一般被认为叫系统占用的端口
            System.out.println("服务器启动完毕");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /** 该方法用于启动服务端程序的执行 */
    public void start(){
        try {
            while (true){
                System.out.println("等待客户端连接---");
                Socket socket =serverSocket.accept();
            /*
            Socket.accpet():
            用于接受客户端的连接，并返回一个Socket实例与连接的客户端交互
            该方法是一个阻塞方法，调用后程序会"卡住"，直到一个客户端连接为止
            跟Scanner输入一样
             */
                //启动一个线程来处理该客户端的交互
                System.out.println("一个客户端连接了");
                ClientHandler handler =new ClientHandler(socket);
                Thread t =new Thread(handler);
                t.start();

                /*String message =br.readLine(); //读取一行来自远端计算机发生过来的字符串
                System.out.println("客户端说:"+message);*/
            }

        } catch (IOException e) {
            e.printStackTrace();

        }
    }

    public static void main(String[] args) {
        Server server = new Server();//自动调用构造方法
        server.start();
    }
//该线程任务用于负责与指定的客户端交互
private class ClientHandler implements Runnable{
        private  Socket socket;
        public ClientHandler(Socket socket){
            this.socket=socket;
        }
    @Override
    public void run() {
        try {
            InputStream in=socket.getInputStream();
            InputStreamReader isr =new InputStreamReader(in);
            BufferedReader br =new BufferedReader(isr);
                /*
                当我们通过br.readLine()读取对方发送过来的一行字符串时,
                远端计算机如果调用scoket.close()正常与我们断开连接(进行四次挥手)
                那么readLine()方法会返回null


                若对方时是异常中断(强制杀死进行)，此时readLine()会抛出异常
                 */
            String message;
            while ((message= br.readLine())!=null){//读取客户端发送过来的消息
                System.out.println("客户端说:"+message);
                //BufferedReader.readLine() 返回 null 仅在一种场景下发生：
                //当客户端主动关闭连接（调用 socket.close() 或程序正常退出）时，底层输入流会检测到 “连接已断开”，
                // 此时 readLine() 会返回 null，表示 “没有更多数据了”。
                //换句话说：
                //如果客户端没调用 socket.close()，且连接正常（网络没断、客户端程序没崩溃），readLine() 就不会返回 null，
                // 会一直 “阻塞” 等待客户端发新数据。
                //只有客户端主动断开（执行 socket.close()），才会触发 readLine() 返回 null，让服务端的 while 循环退出。
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
}
